﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

namespace AutoIt_IDE.Core
{
    public class ModuleManager
    {
		public delegate void ModuleLoadErrorEventHandler(String ModuleName, String ProblemDescription);
		public event ModuleLoadErrorEventHandler ModuleLoadError;

		private const string ERROR_MODULEFOLDERDOESNOTEXIST = "specified module folder does not exist";
		private const string ERROR_MODULEHARDDEPENDENCYRESOLVEPROBLEM = "unable to resolve a hard dependency of the module. hard dependency name: {0}";

        private List<ModuleBase> _LoadedModules = new List<ModuleBase>();

		public ModuleManager()
		{
			
		}

		internal void AddBaseModule(ModuleBase BaseModule)
		{
			this._LoadedModules.Add(BaseModule);
		}

		public void LoadModules(List<string> ModuleFiles)
		{
			if((ModuleFiles != null) && (ModuleFiles.Count > 0))
			{
				foreach(String ModuleFile in ModuleFiles)
				{
					LoadModulesFromFile(ModuleFile);
				}

				bool AllModuleDependenciesResolved = false;
				do
				{//HACK just simply recursive "removing" all modules where the hard dependencies couldn't be resolved
					foreach(ModuleBase tmpMod in this._LoadedModules)
					{
						AllModuleDependenciesResolved = CheckModuleDependencies(tmpMod);
					}
				} while(!AllModuleDependenciesResolved);

				StartModules();
			}
		}

		private void StartModules()
		{
			foreach(ModuleBase tmpMod in this._LoadedModules)
			{
				StartModule(tmpMod);
			}
		}

		private void StartModule(ModuleBase mod)
		{
			if(!mod.IsStarted)
			{
				String[] HardDependencies = mod.GetHardDependencies();
				bool allDependenciesOK = false;
				if((HardDependencies != null) && (HardDependencies.Length > 0))
				{
					foreach(String dependencyName in HardDependencies)
					{
						ModuleBase tmpDependencyModule = GetModuleByName(dependencyName);
						if(!tmpDependencyModule.IsStarted)
						{
							StartModule(tmpDependencyModule);
							if(!tmpDependencyModule.IsStarted)
								throw new ModuleStartException(dependencyName);
						}
					}
				}
				else
				{
					allDependenciesOK = true;
				}

				if(allDependenciesOK)
				{
					mod.Start();
				}
			}
		}

		private ModuleBase GetModuleByName(String ModuleName)
		{
			ModuleBase retVal = null;

			foreach(ModuleBase tmpMod in this._LoadedModules)
			{
				if(tmpMod.GetName() == ModuleName)
				{
					retVal = tmpMod;
					break;
				}
			}

			return retVal;
		}

		private bool CheckModuleDependencies(ModuleBase tmpMod)
		{
			bool retVal = true;

			String[] HardDependencies = tmpMod.GetHardDependencies();
			if((HardDependencies != null) && (HardDependencies.Length > 0))
			{
				foreach(String tmpDependencyName in HardDependencies)
				{
					if(!this.GetLoadedModuleNames().Contains(tmpDependencyName))
					{
						this._LoadedModules.Remove(tmpMod);

						if(ModuleLoadError != null)
							ModuleLoadError(tmpMod.GetName(), String.Format(ERROR_MODULEHARDDEPENDENCYRESOLVEPROBLEM, tmpDependencyName));

						retVal = false;

						break;
					}
				}
			}

			return retVal;
		}

		private void LoadModulesFromFile(String file)
		{
			Assembly asm = Assembly.LoadFile(file);
			Type[] asmTypes = asm.GetTypes();

			if(asmTypes.Length > 0)
			{
				List<Type> possibleModules = new List<Type>();
				foreach(Type tmpAsmType in asmTypes)
				{
					if(typeof(ModuleBase).IsAssignableFrom(tmpAsmType))
					{
						possibleModules.Add(tmpAsmType);
					}
				}

				if(possibleModules.Count > 0)
				{
					foreach(Type mod in possibleModules)
					{
						if(typeof(ModuleBase).IsAssignableFrom(mod))
						{
							ModuleBase tmpMod = (ModuleBase)Activator.CreateInstance(mod);
							this._LoadedModules.Add(tmpMod);
						}
					}
				}
			}
		}

        public List<String> GetLoadedModuleNames()
        {
            List<String> loaded = new List<String>();

            foreach (ModuleBase mod in this._LoadedModules)
            {
                loaded.Add(mod.GetName());
            }

            return loaded;
        }

		public List<ModuleBase> GetLoadedModules()
		{
			return this._LoadedModules;
		}

		[Serializable]
		public class ModuleStartException : Exception
		{
			public ModuleStartException()
			{
			}

			public ModuleStartException(string ModuleName)
				: base("module-name: " + ModuleName)
			{
			}

			public ModuleStartException(string ModuleName, Exception inner)
				: base("module-name: " + ModuleName, inner)
			{
			}

			protected ModuleStartException(
			  System.Runtime.Serialization.SerializationInfo info,
			  System.Runtime.Serialization.StreamingContext context)
				: base(info, context)
			{
			}
		}
    }
}
